Volver al Artículo
Lectura de datos
Descargar código fuente

Lectura de datos

Fecha de publicación

27 de junio de 2025

Los datos se dividen en dos grupos, datos numéricos y ráster, de acuerdo al formato en el que se presentan. Esto determina los paquetes requeridos y las operaciones necesarias para trabajar con estos.

Datos numéricos

Estos datos son aquellos que se registran en un archivo Excel, que incluyen los datos recolectados in situ al momento de tomar la muestra de agua sobre el río Paraná; y los resultados de los ensayos de laboratorio.

  • In situ: latitud y longitud geográfica, profundidad de disco de Secchi, conductividad y pH.

  • Ensayos de laboratorio: sólidos suspendidos y turbidez.

In [1]:
datos <- readxl::read_xlsx(
  path = x,
  sheet = 1,
  .name_repair = "unique_quiet"
) |>
  select(
    fecha = 1, longitud = 4, latitud = 5,
    ph = 6, cond = 8, secchi = 10,
    sol_sus = 12, turb = 13, hazemeter = 14
  ) |>
  fill(fecha) |>
  mutate(fecha = ymd(fecha)) |>
  pivot_longer(
    cols = -c(fecha, latitud, longitud),
    names_to = "param",
    values_to = "valor"
  ) |>
  drop_na(valor, latitud, longitud)

write_csv(datos, "datos/base_de_datos_lab.csv")
1
Selecciono las columnas de interés y cambio de nombre.
2
Transformo a tabla larga para una mejor organización de los datos.
3
Almaceno los resultados en una base de datos.

Los datos presentan la siguiente estructura.

In [2]:

readr::read_csv(
  file = "../datos/base_de_datos_lab.csv",
  show_col_types = FALSE
) |>
  head()
# A tibble: 6 × 5
  fecha      longitud latitud param   valor
  <date>        <dbl>   <dbl> <chr>   <dbl>
1 2023-05-11    -58.9   -27.5 ph        6.8
2 2023-05-11    -58.9   -27.5 cond    141. 
3 2023-05-11    -58.9   -27.5 sol_sus 198. 
4 2023-05-11    -58.9   -27.5 turb    186. 
5 2023-05-11    -58.9   -27.5 ph        6.8
6 2023-05-11    -58.9   -27.5 cond    120. 

Ráster

Las imágenes satelitales se obtienen a partir del catálogo Copernicus Data Space. La solicitud require un rango de fecha alrededor de la adquisición de la escena, las coordenadas de la imagen, la colección de interés y el nivel de procesamiento (L1C, en reflectancia a tope de atmósfera).

Es requisito poseer una cuenta de usuario para acceder a la descarga del producto.

In [3]:
import requests
import pandas as pd
from datetime import datetime, timedelta
import os
import certifi
import json

catalogue_odata_url = "https://catalogue.dataspace.copernicus.eu/odata/v1"

collection_name = "SENTINEL-2"
product_type = "S2MSI1C"
max_cloud_cover = 1
aoi = "POINT(-58.81348666883592 -27.488354054598737)"
search_period_start = "2024-01-01T00:00:00.000Z"
search_period_end = "2024-01-02T00:00:00.000Z"
1
Cargo todas las librerías.
2
Indico colección, nivel de procesamiento, ubicación del tile y rango de fechas.

Con los datos mostrados se crea un query y con las credenciales de usuario se accede mediante el token generado.

In [4]:
search_query = f"{catalogue_odata_url}/Products?$filter=Collection/Name eq '{collection_name}' and Attributes/OData.CSC.StringAttribute/any(att:att/Name eq 'productType' and att/OData.CSC.StringAttribute/Value eq '{product_type}') and OData.CSC.Intersects(area=geography'SRID=4326;{aoi}') and ContentDate/Start gt {search_period_start} and ContentDate/Start lt {search_period_end}"

response = requests.get(search_query).json()
result = pd.DataFrame.from_dict(response["value"])

username = os.environ["COPERNICUS_USERNAME"]
password = os.environ["COPERNICUS_PASSWORD"]

auth_server_url = "https://identity.dataspace.copernicus.eu/auth/realms/CDSE/protocol/openid-connect/token"
data = {
    "client_id": "cdse-public",
    "grant_type": "password",
    "username": username,
    "password": password,
}

response_cred = requests.post(
    auth_server_url, data=data, verify=True, allow_redirects=False
)
access_token = json.loads(response_cred.text)["access_token"]
1
Transformo la respuesta del servidor a tabla.
2
Credenciales de usuario.

La descarga se realiza a partir de un loop en caso de existir múltiples productos disponibles.

In [5]:
if len(result) == 0:
    print(
      "\n\n--- NO HAY PRODUCTO DISPONIBLE PARA EL DÍA DE LA FECHA ---\n\n"
    )
elif os.path.isfile("producto/producto.zip") == True:
    print("\n\n--- PRODUCTO YA DESCARGADO ---\n\n")
else:
    producto_id = result["Id"][0]
    producto_nombre = result["Name"][0]

    print("ID del producto", producto_id)
    print("Nombre del producto", producto_nombre)

    # URL de descarga del producto
    url = f"https://zipper.dataspace.copernicus.eu/odata/v1/Products({producto_id})/$value"

    headers = {"Authorization": f"Bearer {access_token}"}

    session = requests.Session()
    session.headers.update(headers)
    response_prod = session.get(url, headers=headers, stream=True)

    print("\n\n--- DESCARGANDO PRODUCTO ---\n\n")

    with open("producto/producto.zip", "wb") as file:
        for chunk in response_prod.iter_content(chunk_size=8192):
            if chunk:
                file.write(chunk)

    print("\n\n--- PRODUCTO DESCARGADO ---\n\n")
1
En caso que no haya resultados de búsqueda, se detiene el loop con un mensaje.
2
Si el producto fue previamente descargado, que se detiene el loop.
3
Almaceno el producto como archivo comprimido (.zip).

La documentación de la API resultó de gran ayuda para este desarrollo.